package net.moonbiter.ebs.exposer;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.moonbiter.ServletUtils;

import org.apache.log4j.Logger;

import net.moonbiter.ebs.InputDef;
import net.moonbiter.ebs.ServiceCaller;
import net.moonbiter.ebs.ServiceDef;
import net.moonbiter.ebs.ServicesRegister;
import net.moonbiter.ebs.callers.DebugHttpCaller;
import net.moonbiter.ebs.callers.ComponibleServiceCaller;
import net.moonbiter.ebs.callers.HttpParamsInputInterpreter;
import net.moonbiter.ebs.callers.InputInterpreter;
import net.moonbiter.ebs.callers.JsonDeliverer;
import net.moonbiter.ebs.callers.OutputDeliverer;
import net.moonbiter.ebs.treebuilding.UploadedFileTreeBuilder;

/**
 * Servlet implementation class for Servlet: EbsExposerServlet
 *
 */
public class EbsExposerServlet extends javax.servlet.http.HttpServlet implements
		javax.servlet.Servlet {
	private static final long serialVersionUID = 6801615007008357258L;
	private final static Logger logger = Logger.getLogger(EbsExposerServlet.class);
	private ServicesRegister servicesRegister;
	private DebugHttpCaller serviceCaller;
	private ServiceCaller<HttpServletRequest,HttpServletResponse> httpJsonCaller;

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		servicesRegister = new ServicesRegister();
		servicesRegister.register(new LoggerService());
		servicesRegister.register(new RubricaService());
		servicesRegister.register(new DebugService());
		servicesRegister.register(new CounterService());
		
		File tmpFilesDir = new File(config.getServletContext().getRealPath("/WEB-INF/temp"));
		if (!tmpFilesDir.exists()){
			if (!tmpFilesDir.mkdir()){
				throw new ServletException("Can't create the directory for temporary uploaded files");
			}
		}
		
		serviceCaller = new DebugHttpCaller(tmpFilesDir,
				1024^2,1024^3);
		InputInterpreter<HttpServletRequest> httpInterp = new HttpParamsInputInterpreter(
				tmpFilesDir,
				1024^2,1024^3);
		OutputDeliverer<HttpServletResponse> jsonDeliv = new JsonDeliverer();
		httpJsonCaller = new ComponibleServiceCaller(
				httpInterp,jsonDeliv);
	}
	
	@Override
	public void destroy() {
		// TODO cancella i file temporanei
		super.destroy();
	}

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	private void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		String relativeUri = ServletUtils.relativeUri(request);
		logger.debug("Received request per relative URI : "+relativeUri);
		
		String[] portions = relativeUri.split("/");
		if (portions.length<2) response.setStatus(HttpServletResponse.SC_NOT_FOUND);
		
		String action = portions[1];
		if (action.equals("doc")){
			if (!tryToShowDoc(relativeUri, request, response)){
				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			}
		}
		
		if (action.equals("http-json")){
			try {
				if (!tryToInvokeService(httpJsonCaller,relativeUri, request, response)){
					response.setStatus(HttpServletResponse.SC_NOT_FOUND);
				}
			} catch (Exception e) {
				throw new ServletException(e);
			}
		}
			
		response.setStatus(HttpServletResponse.SC_NOT_FOUND);		
	}
	
	private boolean tryToShowDoc(String relativeUri, HttpServletRequest request,HttpServletResponse response){
		// URL che richiama la documentazione:
		// /doc/providerName/serviceName.ebs
		String[] portions = relativeUri.split("/");
		if (4!=portions.length){
			return false;
		}
		String action = portions[1];
		if (!action.equals("doc")){
			return false;
		}
		String providerName = portions[2];
		String serviceName = portions[3].substring(0,portions[3].length()-4);
		
		if (!servicesRegister.hasService(providerName, serviceName)){
			logger.debug("Required docs for an unexisting service, providerName=\""
					+providerName+"\", serviceName=\""+serviceName+"\"");
			// response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return false;
		} else {
			logger.debug("Required docs for an existing service, providerName=\""
					+providerName+"\", serviceName=\""+serviceName+"\"");
			ServiceDef serviceDef = servicesRegister.getService(providerName, serviceName);
			
			showDoc(response, serviceDef,providerName, serviceName);
			return true;
		}
	}
	
	private boolean tryToInvokeService(ServiceCaller servCaller, String relativeUri, HttpServletRequest request,HttpServletResponse response) throws Exception{
		// URL che richiama un serivizio:
		// /providerName/serviceName.ebs
		String[] portions = relativeUri.split("/");
		if (4!=portions.length){
			// response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return false;
		}
		String providerName = portions[2];
		String serviceName = portions[3].substring(0,portions[3].length()-4);
		
		if (!servicesRegister.hasService(providerName, serviceName)){
			logger.debug("Required unexisting service, providerName=\""
					+providerName+"\", serviceName=\""+serviceName+"\"");
			// response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return false;
		} else {
			logger.debug("Required existing service, providerName=\""
					+providerName+"\", serviceName=\""+serviceName+"\"");
			ServiceDef serviceDef = servicesRegister.getService(providerName, serviceName);
			
			servCaller.execute(request, response, serviceDef);
			return true;
		}
	}
	
	private void showDoc(HttpServletResponse response,ServiceDef servDef,
			String providerName, String serviceName){
		PrintWriter writer;
		try {
			writer = response.getWriter();
			writer.println("<h1>"+providerName+"."+serviceName+"</h1>");
			
			Map<String,InputDef> inputs = servDef.getInputs();
			writer.println("<i>"+inputs.keySet().size()+" inputs</i>");
			
			int index = 0;
			for (String inputName : inputs.keySet()){
				showInputDefDoc(writer, index,inputName,inputs.get(inputName));
			}
			
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	
	private void showInputDefDoc(PrintWriter writer, int index, String inputName, InputDef inputDef){
		writer.println("<h3>Param "+(index+1)+") "+inputName+"<h3>");
		writer.println("&nbsp;&nbsp;&nbsp;java class: "+inputDef.getType().getCanonicalName()+", <i>optional? "+inputDef.isOptional()+"</i>");
	}
}